package com.cougil.uoc.seghismed.business.impl;

import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.persistence.Query;

import com.cougil.uoc.seghismed.business.MetgeService;
import com.cougil.uoc.seghismed.model.Metge;
import com.cougil.uoc.seghismed.model.Usuari;
import com.cougil.uoc.seghismed.model.ciphed.MetgeCiphed;
import com.cougil.uoc.seghismed.model.ciphed.PacientCiphed;

/**
 * Classe que implementa els mètodes oferts a les peticions rebudes a través dels aplicatius dels
 * Metges. També disposa de la lògica vinculada amb la gestió de la informació relacionada amb els
 * Metges
 * 
 * @author Ignacio Cougil Jares
 *
 */
public class MetgeServiceImpl extends CoreServerServiceImpl implements MetgeService {

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#consultarMetge(com.cougil.uoc.seghismed.model.ciphed.MetgeCiphed)
	 */
	@Override
	public MetgeCiphed consultarMetge(MetgeCiphed mCiphed) {
		Metge metge = this.cipherService.desxifrarMetge(mCiphed, this.getGestor());
		// cerquem el metge a través del seu DNI
		Metge m = this.findByDni(metge.getDni());
		// xifrem el propi metge emprant la clau privada del gestor i la clau pública del propi metge
		return this.cipherService.xifrarMetge(m, this.getGestor(), m);

	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#consultarPacients(com.cougil.uoc.seghismed.model.ciphed.MetgeCiphed)
	 */
	@Override
	public List<PacientCiphed> consultarPacients(MetgeCiphed metgeCiphed) {
		Metge metge = this.cipherService.desxifrarMetge(metgeCiphed, this.getGestor());
		CoreServerServiceImpl.logger.debug("Intentant obtenir tots els pacients del metge ["+metge+"]");
		// cerquem el metge a través del seu DNI
		metge = this.findByDni(metge.getDni());
		List<PacientCiphed> pacientsCiphed = null;
		if (metge != null) {
			// retornem la llista de pacients associats al metge, xifrats només per al gestor
			pacientsCiphed = metge.getLlistaPacientsProt();
			// desxifrem la llista de pacients protegits per al gestor i la xifrem per al destinatari
			// pacientsCiphed = this.cipherService.xifrarPacientsProtegits(metge.getLlistaPacientsProt(), this
			// .getGestor(), metge);
			CoreServerServiceImpl.logger.debug(pacientsCiphed.size() + " pacients assignats al metge ["
					+ metge + "]");
		} else {
			CoreServerServiceImpl.logger.debug("Cap pacient assignat al metge ["+metge+"]");
		}
		return pacientsCiphed;
	}

	private void findAndRemoveMetgeCiphed(Usuari metge) {
		CoreServerServiceImpl.logger.debug("Intentant esborrar el metge xifrat associat al metge ["
				+ metge.getId() + "]");
		List<MetgeCiphed> metgesXifrats = this.findMetgesCiphed();
		MetgeCiphed pc = this.findMetgeCiphed(metgesXifrats, metge);
		if (pc != null) {
			// eliminem finalment el metge xifrat
			this.em.remove(pc);
			CoreServerServiceImpl.logger.debug("Metge xifrat associat al metge [" + metge.getId()
					+ "] esborrat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Metge xifrat associat al metge [" + metge.getId()
					+ "] no esborrat!");
		}
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#findByDni(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public Metge findByDni(String dni) {
		CoreServerServiceImpl.logger.debug("Intentant cercar el metge amb dni [" + dni + "]");
		Metge m = null;
		Query query = this.em.createQuery("SELECT m FROM Metge m WHERE m.dni = :dni");
		query.setParameter("dni", dni);
		List<Metge> llista = query.getResultList();
		if (llista.size() > 0) {
			m = llista.get(0);
			CoreServerServiceImpl.logger.debug("Metge ["+m+"] trobat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Metge amb DNI ["+dni+"] no trobat!");
		}
		return m;
	}


	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#find(java.lang.Integer)
	 */
	public Metge findById(Integer id) {
		CoreServerServiceImpl.logger.debug("Intentant cercar el metge amb identificador [" + id
				+ "]");
		Metge p = this.em.find(Metge.class, id);
		if (p != null) {
			CoreServerServiceImpl.logger.debug("Metge ["+p+"] trobat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Metge ["+id+"] no trobat!");
		}
		return p;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#findLastMetgeCiphed()
	 */
	@SuppressWarnings("unchecked")
	public MetgeCiphed findLastMetgeCiphed() {
		CoreServerServiceImpl.logger.debug("Intentant cercar l'últim metge xifrat");
		MetgeCiphed mc = null;
		Query query = this.em
		.createQuery("SELECT mc FROM MetgeCiphed mc WHERE mc.id = (SELECT MAX(id) FROM MetgeCiphed)");
		List<MetgeCiphed> llista = query.getResultList();
		if (llista.size() > 0) {
			mc = llista.get(0);
			CoreServerServiceImpl.logger.debug("Metge xifrat [" + mc + "] trobat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Metge xifrat no trobat!");
		}
		return mc;
	}

	/**
	 * Retorna el metge xifrat que representa el metge informat
	 *
	 * @param metgesXifrats
	 *            {@link List} llista de metges xifrats sobre els que cercar el metge informat
	 * @param metge
	 *            {@link Metge} metge a cercar
	 * @return metge xifrat {@link MetgeCiphed} trobat o null en cas contrari
	 */
	private MetgeCiphed findMetgeCiphed(List<MetgeCiphed> metgesXifrats, Usuari metge) {
		CoreServerServiceImpl.logger.debug("Intentant cercar el metge xifrat associat al metge ["
				+ metge.getId() + "]");
		MetgeCiphed mc = null;
		boolean trobat = false;
		for (Iterator<MetgeCiphed> iterator = metgesXifrats.iterator(); iterator.hasNext() && !trobat;) {
			mc = iterator.next();
			Metge mDesxifrat = this.cipherService.desxifrarMetge(mc, this.getGestor());
			trobat = mDesxifrat.getId().equals(metge.getId());
		}
		if (trobat) {
			CoreServerServiceImpl.logger.debug("Metge xifrat associat al metge [" + metge.getId()
					+ "] trobat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Metge xifrat associat al metge [" + metge.getId()
					+ "] no trobat!");
			mc = null;
		}
		return mc;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#findMetgeCiphed(com.cougil.uoc.seghismed.model.Metge)
	 */
	@Override
	public MetgeCiphed findMetgeCiphed(Metge metge) {
		CoreServerServiceImpl.logger.debug("Intentant cercar el metge xifrat associat al metge ["
				+ metge.getId() + "]");
		List<MetgeCiphed> metgesXifrats = this.findMetgesCiphed();
		return this.findMetgeCiphed(metgesXifrats, metge);
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#findAll()
	 */
	@SuppressWarnings("unchecked")
	public List<Metge> findMetges() {
		CoreServerServiceImpl.logger.debug("Intentant cercar tots els metges");
		Query query = this.em.createQuery("SELECT m FROM Metge m");
		List<Metge> llista = query.getResultList();
		CoreServerServiceImpl.logger.debug(llista.size()+" metges trobats satisfactoriament");
		return llista;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#findMetgesCiphed()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<MetgeCiphed> findMetgesCiphed() {
		CoreServerServiceImpl.logger.debug("Intentant cercar tots els metges xifrats");
		Query query = this.em.createQuery("SELECT mc FROM MetgeCiphed mc");
		List<MetgeCiphed> llista = query.getResultList();
		CoreServerServiceImpl.logger.debug(llista.size()+" metges xifrats trobats satisfactoriament");
		return llista;
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#remove(java.lang.Integer)
	 */
	public void remove(Integer id) {
		CoreServerServiceImpl.logger.debug("Intentant esborrar el metge amb id: " + id);
		Usuari metge = this.findById(id);
		if (metge != null) {
			// veure métode PacientServiceImpl.findPacientCiphedByIdCiphed()
			/* 	Cal esborrar aquell metge xifrat que el seu ID coincideixi amb la que es preten esborrar.
			En realitat hauríem de fer servir els mètodes que venen a continuació, perqué aquest métode no és del tot
			eficient, donat que desxifra cadascun dels metges xifrats un per un fins que trobi un que el seu ID
			coincideixi amb l'ID del metge */
			this.findAndRemoveMetgeCiphed(metge);

			// finalment eliminem el metge
			this.em.remove(metge);

			CoreServerServiceImpl.logger.debug("Metge ["+metge+"] esborrat satisfactoriament");
		} else {
			CoreServerServiceImpl.logger.debug("Metge ["+id+"] no esborrat!");
		}
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#removeAll()
	 */
	public int removeAll() {
		/*
		 * La següent SQL no funciona:
		 *
		 * Query query = em.createQuery("DELETE FROM Metge m");
		 *
		 * Amb Hibernate 3.2.1.ga remet el següent error:
		 *
		 * Caused by: org.hibernate.exception.SQLGrammarException: could not insert/select
		 * ids for bulk delete ... Caused by:
		 * com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table
		 * 'seghismed.ht_metge' doesn't exist
		 *
		 * Amb Hibernate 3.3.2.GA remet el següent error (més correcte, queixant-se de
		 * existeixen registres fills, però passa del CASCADE.ALL):
		 *
		 * Cannot delete or update a parent row: a foreign key constraint fails
		 * (`seghismed/metge_pacientciphed`, CONSTRAINT `FK_REFERENCE_7` FOREIGN KEY
		 * (`IDMETGE`) REFERENCES `metge` (`ID`))
		 *
		 * Solució salomònica: recorrem tots els metges un per un i els esborrem un per un
		 */
		CoreServerServiceImpl.logger.debug("Intentant esborrar tots els metges");
		List<Metge> llista = this.findMetges();
		int i = 0;
		for (Iterator<Metge> iterator = llista.iterator(); iterator.hasNext();) {
			Usuari metge = iterator.next();
			this.remove(metge.getId());
			i++;
		}
		CoreServerServiceImpl.logger.debug(i+" metges esborrats satisfactoriament");
		return i;
	}

	private void save(List<PacientCiphed> pacients) {
		CoreServerServiceImpl.logger.debug("Intentant emmagatzemar la col·leció de pacients protegits: "
				+ pacients);
		for (Iterator<PacientCiphed> iterator = pacients.iterator(); iterator.hasNext();) {
			PacientCiphed pacientCiphed = iterator.next();
			// nou pacient xifrat
			if (pacientCiphed.getId() == null) {
				this.em.persist(pacientCiphed);
				CoreServerServiceImpl.logger.debug("Pacient xifrat [" + pacientCiphed
						+ "] emmagatzemat satisfactoriament");
				// el pacient ja existeix, l'actualitzem
			} else {
				this.em.merge(pacientCiphed);
				CoreServerServiceImpl.logger.debug("Pacient xifrat [" + pacientCiphed
						+ "] actualitzat satisfactoriament");
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#save(com.cougil.uoc.seghismed.model.Metge)
	 */
	public void save(Metge metge) {
		CoreServerServiceImpl.logger.debug("Intentant emmagatzemar el metge: "+metge);
		// nou metge
		if (metge.getId() == null) {
			if (metge.getDataAlta() == null) {
				metge.setDataAlta(new Date());
			}
			this.em.persist(metge);
			CoreServerServiceImpl.logger.debug("Metge ["+metge+"] emmagatzemat satisfactoriament");

			// cal instanciar un objecte MetgeCiphed que representa el metge xifrat només pel gestor
			MetgeCiphed mc = this.cipherService.xifrarMetge(metge, this.getGestor(), this.getGestor());
			// MetgeCiphed mc = this.cipherService.signarXifrarMetge(metge, this.getGestor(), this.getGestor());
			this.save(mc);

			// el metge ja existeix, l'actualitzem
		} else {
			metge.setDataModificacio(new Date());
			this.em.merge(metge);
			CoreServerServiceImpl.logger.debug("Metge ["+metge+"] actualitzat satisfactoriament");
		}

		// guardem la seva llista de pacients protegits
		// this.save(metge.getLlistaPacientsProt());
	}

	private void save(MetgeCiphed mc) {
		CoreServerServiceImpl.logger.debug("Intentant emmagatzemar el metge xifrat: " + mc);
		if (mc.getId() == null) {
			this.em.persist(mc);
			CoreServerServiceImpl.logger.debug("Metge xifrat ["+mc+"] emmagatzemat satisfactoriament");

		} else {
			this.em.merge(mc);
			CoreServerServiceImpl.logger.debug("Metge xifrat ["+mc+"] actualitzat satisfactoriament");
		}
	}

	/* (non-Javadoc)
	 * @see com.cougil.uoc.seghismed.business.MetgeService#totalMetges()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public long totalMetges() {
		CoreServerServiceImpl.logger.debug("Intentant comptar tots els metges");
		Query query = this.em.createQuery("SELECT COUNT(*) FROM Metge");
		List<Long> llista = query.getResultList();
		long resultat = llista.get(0).longValue();
		CoreServerServiceImpl.logger.debug(resultat + " metges comptats");
		return resultat;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see com.cougil.uoc.seghismed.business.MetgeService#totalMetgesCiphed()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public long totalMetgesCiphed() {
		CoreServerServiceImpl.logger.debug("Intentant comptar tots els metges xifrats");
		Query query = this.em.createQuery("SELECT COUNT(*) FROM MetgeCiphed");
		List<Long> llista = query.getResultList();
		long resultat = llista.get(0).longValue();
		CoreServerServiceImpl.logger.debug(resultat + " metges xifrats comptats");
		return resultat;
	}

}
